using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Nemerle.Collections;

using Nextem.String;

using IronMacro.Core;

namespace IronMacro.Compiler {
	public class Compiler {
		this(spec : string, ns : string, outFn : string) {
			def spec = Spec.Load(spec);
			
			def fp = File.Open(outFn, FileMode.Create);
			def sw = StreamWriter(fp);
			sw.WriteLine("using Nemerle.Collections;");
			sw.WriteLine("using IronMacro.Core;");
			sw.WriteLine("namespace {0} {{" <- ns);
			
			foreach(child in spec) {
				| EnumMapping(name, _size, members) =>
					sw.WriteLine("\tpublic enum {0} {{" <- name);
					foreach(pair in members)
						sw.WriteLine("\t\t| {0} = {1}" <- (pair.Key, pair.Value[0]));
					sw.WriteLine("\t}")
				
				| Class(name, _size, members) =>
					sw.WriteLine("\tpublic class {0} {{" <- name);
					sw.WriteLine("\t\tOff : int * int;");
					sw.WriteLine("\t\tWin : Window;");
					sw.WriteLine("\t\tPath : string;");
					sw.WriteLine("\t\tpublic this(off : int * int, win : Window, path : string) {");
					sw.WriteLine("\t\t\tOff = off;");
					sw.WriteLine("\t\t\tWin = win;");
					sw.WriteLine("\t\t\tPath = path;");
					foreach(pair in members)
						match(pair.Value[1]) {
							| _ when pair.Value[0][0] == -1 => ()
							| Ref(_, Class(cname, _, _)) => 
								sw.WriteLine(
									"\t\t\t{0} = {1}((Off[0]+{2}, Off[1]+{3}), Win, Path);" <- (
											pair.Key, 
											cname, 
											pair.Value[0][0], 
											pair.Value[0][1]
										)
								)
							| _ => ()
						}
					sw.WriteLine("\t\t}");
					sw.WriteLine("\t\tpublic CheckTriggers() : void {");
					foreach(pair in members)
						match(pair.Value[1]) {
							| _ when pair.Value[0][0] == -1 => ()
							| Ref(_, Class) => 
								sw.WriteLine("\t\t\t{0}.CheckTriggers();" <- pair.Key)
							| Trigger(name, on, image) when image != null =>
								match(on) {
									| Change => ()
									| _ =>
										def rect = "(Off[0]+{0}, Off[1]+{1}, Off[0]+{2}, Off[1]+{3})" <- (pair.Value[0][0], pair.Value[0][1], pair.Value[0][2], pair.Value[0][3]);
										sw.WriteLine("\t\t\tmatch(Trigger{0}State) {{" <- name);
										sw.WriteLine(
											"\t\t\t\t| Appear when !Matching.MatchInvMask(Win, {0}, Path+\"\\\\{1}\") =>" <- (
													rect, image
												)
										);
										sw.WriteLine("\t\t\t\t\tTrigger{0}State = TriggerOn.Disappear;" <- name);
										when(on == TriggerOn.Disappear || on == TriggerOn.Both)
											sw.WriteLine("\t\t\t\t\t{0}()" <- name);
										sw.WriteLine(
											"\t\t\t\t| Disappear when Matching.MatchInvMask(Win, {0}, Path+\"\\\\{1}\") =>" <- (
													rect, image
												)
										);
										sw.WriteLine("\t\t\t\t\tTrigger{0}State = TriggerOn.Appear;" <- name);
										when(on == TriggerOn.Appear || on == TriggerOn.Both)
											sw.WriteLine("\t\t\t\t\t{0}()" <- name);
										sw.WriteLine("\t\t\t\t| _ => ()");
										sw.WriteLine("\t\t\t}")
								}
							
							| _ => ()
						}
					sw.WriteLine("\t\t}");
					foreach(pair in members)
						match(pair.Value[1]) {
							| _ when pair.Value[0][0] == -1 => ()
							| Trigger(name, on, _) =>
								sw.WriteLine("\t\tpublic event {0} : TriggerDelegate;" <- name);
								match(on) {
									//| Change => sw.WriteLine("\t\tmutable Trigger{0}Image : Bitmap;" <- name)
									| _ => sw.WriteLine("\t\tpublic mutable Trigger{0}State : TriggerOn = TriggerOn.Disappear;" <- name)
								}
							| Ref(_, Class(cname, _, _)) =>
								sw.WriteLine("\t\tpublic {0} : {1};" <- (pair.Key, cname))
							| spec => 
								sw.WriteLine(
									"\t\tpublic {0} : {1} {{" <- (
											pair.Key, 
											match(spec) {
												| StringMapping => "string"
												| IntMapping => "int"
												| BoolMapping => "bool"
												| Ref(_, spec) => spec.Name
												| _ => null
											}
										)
								);
								sw.WriteLine("\t\t\tget {");
								sw.WriteLine(
									"\t\t\t\tMatching.MatchAll(Win, ({0} + Off[0], {1} + Off[1], {2} + Off[0], {3} + Off[1]), Path, Hashtable([" <- (
											pair.Value[0][0], pair.Value[0][1], pair.Value[0][2], pair.Value[0][3]
										)
								);
								match(spec) {
									| Ref(_, EnumMapping(name, _, members)) =>
										foreach(pair in members)
											sw.WriteLine(
												"\t\t\t\t\t(\"{0}\", {1}.{2}), " <- (pair.Value[1], name, pair.Key)
											)
									| StringMapping(_, mapping) =>
										foreach(pair in mapping)
											sw.WriteLine(
												"\t\t\t\t\t(\"{0}\", \"{1}\"), " <- (pair.Value, pair.Key)
											)
									| IntMapping(_, mapping) =>
										foreach(pair in mapping)
											sw.WriteLine(
												"\t\t\t\t\t(\"{0}\", {1}), " <- (pair.Value, pair.Key)
											)
									| _ => ()
								}
								sw.WriteLine("\t\t\t\t]))");
								sw.WriteLine("\t\t\t}");
								sw.WriteLine("\t\t}")
						}
					
					sw.WriteLine("\t}")
				
				| _ => ()
			}
			
			sw.WriteLine("}");
			sw.Close();
			fp.Close()
		}
		
		public static Main(args : array [string]) : void {
			_ = Compiler(args[0], args[1], args[2])
		}
	}
}
